﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using PostSharp.Laos;

namespace EntityFrameworkContrib.PostSharp4EF
{
    /// <summary>
    /// interface implemented by complex types
    /// </summary>
    public interface IComplexType : IGetChangeTracker
    {
        void EntityMemberChanged(string scalarPropertyName);
        void EntityMemberChanging(string scalarPropertyName);
        void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker complexChangeTracker);
    }

    /// <summary>
    /// Interface to expose the changetracker
    /// </summary>
    public interface IGetChangeTracker
    {
        IEntityChangeTracker GetChangeTracker();
    }

    #region hookup the implementation to the type
    /// <summary>
    /// implements an interface that will allow us to call into a complex type to do set it's changetracker
    /// </summary>
    [Serializable]
    sealed class PocoComplexInterfacesSubAspect : CompositionAspect
    {
        public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
        {
            return new PocoComplexTypeImplementation(eventArgs.Instance);
        }

        public override Type GetPublicInterface(Type containerType)
        {
            return typeof(IComplexType);
        }

        /// <summary>
        /// Gets weaving options.
        /// </summary>
        /// <returns>Weaving options specifying that the implementation accessor interface (<see cref="IComposed{T}"/>)
        /// should be exposed, and that the implementation of interfaces should be silently ignored if they are
        /// already implemented in the parent types.</returns>
        public override CompositionAspectOptions GetOptions()
        {
            return
                CompositionAspectOptions.GenerateImplementationAccessor |
                CompositionAspectOptions.IgnoreIfAlreadyImplemented;
        }

    } 
    #endregion

    /// <summary>
    /// the implementation object that will be used to delegate all calls to the IComplexType to.
    /// </summary>
	class PocoComplexTypeImplementation : IComplexType
	{
        /// <summary>
        /// your complex type object
        /// </summary>
        private readonly object instance;

        public PocoComplexTypeImplementation(object instance)
        {
            this.instance = instance;
        }

        #region implementation
        public IEntityChangeTracker _complexChangeTracker = null;
        private string _rootComplexPropertyName;

        // Gets an IEntityChangeTracker to call for properties change. 
        // You must do this in order to track changes.
        public void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker complexChangeTracker)
        {
            _rootComplexPropertyName = rootComplexPropertyName;
            _complexChangeTracker = complexChangeTracker;
        }

        // Protected method that is called before the change for change tracking 
        // each of the scalar properties in the complex type.
        public void EntityMemberChanging(string scalarPropertyName)
        {
            if (null != _complexChangeTracker)
            {
                _complexChangeTracker.EntityComplexMemberChanging(_rootComplexPropertyName,
                                                           this, scalarPropertyName);
            }
        }

        // Protected method that is called after the change for change tracking 
        // each of the scalar properties in the complex type.
        public void EntityMemberChanged(string scalarPropertyName)
        {
            if (null != _complexChangeTracker)
            {
                _complexChangeTracker.EntityComplexMemberChanged(_rootComplexPropertyName,
                                                          this, scalarPropertyName);
            }
        }
        #endregion

        #region IGetChangeTracker Members

        IEntityChangeTracker IGetChangeTracker.GetChangeTracker()
        {
            return _complexChangeTracker;
        }

        #endregion
    }
}
